Decorator Pattern 3


In [ ]:
class DecoClass:
    def __init__(self, function):
        self.function = function
        print("DecoClass '__init__' function has been called.")
        
    def __call__(self, *args, **kwargs):
        print("DecoClass has been called.")
        return self.function(*args, **kwargs)

In [ ]:
@DecoClass
def func_1():
    print("'func_1' has been called.")

In [ ]:
func_1()

staticmethod


In [ ]:
class TestClass_1:
    name = "Test_1"
    
    @staticmethod
    def get_name():
        print(TestClass_1.name)

In [ ]:
# 클래스에서 호출
TestClass_1.get_name()

In [ ]:
class TestSub_1(TestClass_1):
    name = "TestSub_1"

In [ ]:
# 부모 클래스의 속성 반환
TestSub_1.get_name()

In [ ]:
class StaticMethod(object):
    "Emulate PyStaticMethod_Type() in Objects/funcobject.c"

    def __init__(self, f):
        self.f = f

    def __get__(self, obj, objtype=None):
        return self.f

루시아누 하말류(전문가를 위한 파이썬)

@classmethod 는 쓰임새가 많은 게 확실하지만, @staticmethod 는 사용해야하는 이유를 잘 모르겠다. 클래스와 함께 작동하지 않는 함수를 정의하려면, 단지 함수를 모듈에 정의하면 된다. 아마 함수가 클래스를 건드리지는 않지만, 그 클래스와 밀접히 연관되어 있어서 클래스 코드 가까운 곳에 두고 싶을 수는 있을 것이다. 그런 경우에는 클래스의 바로 앞이나 뒤에서 함수를 정의하면 된다.

classmethod


In [ ]:
class TestClass_2:
    name = "Test_2"
    
    @classmethod
    def get_name(cls):
        print(cls.name)

In [ ]:
TestClass_2.get_name()

In [ ]:
class TestSub_2(TestClass_2):
    name = "TestSub_2"

In [ ]:
test = TestSub_2()
test.get_name()

In [ ]:
TestSub_2.get_name()

In [ ]:
class ClassMethod(object):
    "Emulate PyClassMethod_Type() in Objects/funcobject.c"

    def __init__(self, f):
        self.f = f

    def __get__(self, obj, klass=None):
        print("ClassMethod")
        print(klass)
        print(obj)
        if klass is None:
            klass = type(obj)
        print(klass)
        def newfunc(*args):
            print(self.f)
            return self.f(klass, *args)
        return newfunc

Practical example

  • Ref. 파이썬 - OOP Part 4. 클래스 메소드와 스태틱 메소드 (School of Web)

1. classmethod를 활용한 기능추가


In [ ]:
class Employee:
    
    raise_amount = 1.1  #1 연봉 인상율 클래스 변수
    
    def __init__(self, first, last, pay):
        self.first = first
        self.last = last
        self.pay = pay
        self.email = first.lower() + '.' + last.lower() + '@schoolofweb.net'
    
    def apply_raise(self):
        self.pay = int(self.pay * self.raise_amount)
        
    def full_name(self):
        return '{} {}'.format(self.first, self.last)
        
    def get_pay(self):
        return '현재 "{}"의 연봉은 "{}"입니다.'.format(self.full_name(), self.pay)

emp_1 = Employee('Sanghee', 'Lee', 50000)
emp_2 = Employee('Minjung', 'Kim', 60000)

모든 인스턴스에 대해 연봉인상률의 일률적 적용


In [ ]:
# 연봉 인상 전
print(emp_1.get_pay())
print(emp_2.get_pay())

# 연봉 인상
emp_1.apply_raise()
emp_2.apply_raise()

# 연봉 인상 후
print(emp_1.get_pay())
print(emp_2.get_pay())

클래스 변수의 직접수정 없이 classmethod를 활용하여 연봉인상률 변경 및 인상률 인자 검증기능 추가


In [ ]:
class Employee:
    
    raise_amount = 1.1  #1 연봉 인상율 클래스 변수
    
    def __init__(self, first, last, pay):
        self.first = first
        self.last = last
        self.pay = pay
        self.email = first.lower() + '.' + last.lower() + '@schoolofweb.net'
    
    def apply_raise(self):
        self.pay = int(self.pay * self.raise_amount)
        
    def full_name(self):
        return '{} {}'.format(self.first, self.last)
        
    def get_pay(self):
        return '현재 "{}"의 연봉은 "{}"입니다.'.format(self.full_name(), self.pay)
        
    #1 클래스 메소드 데코레이터를 사용하여 클래스 메소드 정의
    @classmethod
    def change_raise_amount(cls, amount):
        #2 인상율이 "1" 보다 작으면 재입력 요청
        while amount < 1:
            print('[경고] 인상율은 "1"보다 작을 수 없습니다.')
            amount = input('[입력] 인상율을 다시 입력하여 주십시오.\n=> ')
            amount = float(amount)
        cls.raise_amount = amount
        print('인상율 "{}"가 적용 되었습니다.'.format(amount))

emp_1 = Employee('Sanghee', 'Lee', 50000)
emp_2 = Employee('Minjung', 'Kim', 60000)

In [ ]:
# 연봉 인상 전
print(emp_1.get_pay())
print(emp_2.get_pay())

# 연봉 인상율 변경
Employee.change_raise_amount(0.9)

# 연봉 인상
emp_1.apply_raise()
emp_2.apply_raise()

# 연봉 인상 후
print(emp_1.get_pay())
print(emp_2.get_pay())

2. classmethod를 대체 생성자로 활용

주민등록번호에서 성별 및 생년월일 추출 예시


In [ ]:
class Person:
    def __init__(self, year, month, day, sex):
        self.year = year
        self.month = month
        self.day = day
        self.sex = sex
        
    def __str__(self):
        return '{}{}{}일생 {}입니다.'.format(self.year, self.month, self.day, self.sex)
    
ssn_1 = '900829-1034356'
ssn_2 = '051224-4061569'

def ssn_parser(ssn):
        front, back = ssn.split('-')
        sex = back[0]
        
        if sex == '1' or sex == '2':
            year = '19' + front[:2]
        else:
            year = '20' + front[:2]
            
        if (int(sex) % 2) == 0:
            sex = '여성'
        else:
            sex = '남성'
            
        month = front[2:4]
        day = front[4:6]
        
        return year, month, day, sex

In [ ]:
person_1 = Person(*ssn_parser(ssn_1))
print(person_1)

person_2 = Person(*ssn_parser(ssn_2))
print(person_2)

classmethod를 활용하여 ssn_parser를 클래스 내부에서 정의


In [ ]:
class Person:
    def __init__(self, year, month, day, sex):
        self.year = year
        self.month = month
        self.day = day
        self.sex = sex
        
    def __str__(self):
        return '{}{}{}일생 {}입니다.'.format(self.year, self.month, self.day, self.sex)
    
    @classmethod
    def ssn_constructor(cls, ssn):
        front, back = ssn.split('-')
        sex = back[0]
        
        if sex == '1' or sex == '2':
            year = '19' + front[:2]
        else:
            year = '20' + front[:2]
            
        if (int(sex) % 2) == 0:
            sex = '여성'
        else:
            sex = '남성'
            
        month = front[2:4]
        day = front[4:6]
        
        return cls(year, month, day, sex)
    
    @staticmethod
    def is_work_day(day):
        # weekday() 함수의 리턴값은
        # 월: 0, 화: 1, 수: 2, 목: 3, 금: 4, 토: 5, 일: 6
        if day.weekday() == 5 or day.weekday() == 6:
            return False
        return True
    
ssn_1 = '900829-1034356'
ssn_2 = '051224-4061569'

In [ ]:
person_1 = Person.ssn_constructor(ssn_1)
print(person_1)

person_2 = Person.ssn_constructor(ssn_2)
print(person_2)

staticmethod의 활용

  • 클래스와 연관성이 있는 함수를 클래스 안에서 정의
  • 클래스와 클래스의 인스턴스에서 공유
  • 클래스나 인스턴스를 통해서 호출 가능

In [ ]:
import datetime
# 일요일 날짜 오브젝트 생성
my_date = datetime.date(2016, 10, 9)

# 클래스를 통하여 스태틱 메소드 호출
print(Person.is_work_day(my_date))
# 인스턴스를 통하여 스태틱 메소드 호출
print(person_1.is_work_day(my_date))